/*********************************************************************************
 * Software License Agreement (BSD License)
 * Copyright (c) 2012, Carlos D. Correa (info@ngraph.org)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of netzen nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************************/
#include "glheaders.h"

#include <QApplication>
#include <QImage>
#include <QColor>
#include <QFontDatabase>
#include <QPainter>
#include <QGLWidget>
#include <assert.h>
#include <string.h>
#include <math.h>
#include <typeinfo>
#include <algorithm>
#include <map>


#include "ui/qNWindow.h"
#include "viewManager.h"

#include "controller.h"
#include "renderUtils.h"
#include "graph.h"
#include "mouseUtils.h"

#include "layouts/fdlayout.h"
#include "layouts/linloglayout.h"
#include "layouts/propertyLayout.h"

#ifdef USING_GMPUFM3
#include "layouts/fm3layout.h"
#endif

#include "tools/dataTools.h"
#ifdef USING_LIBPNG
#include "tools/screenShotTool.h"
#endif//def USING_LIBPNG
#include "tools/centrality.h"
#include "tools/clusteringCoefficient.h"
#include "tools/mst.h"
#include "tools/sensitivity.h"
#include "extra/monopoly.h"

#include "views/viewcreators.h"
#include "views/graphView.h"
extern View *createSimpleGraphView(string name);

//#include "views/tableView.h" // 0613

using namespace std;



#if !defined(GLUT_WHEEL_UP)
#  define GLUT_WHEEL_UP   3
#  define GLUT_WHEEL_DOWN 4
#endif

int W = 1200;
int H = 900;

#ifndef M_PI
#define M_PI 3.14159265
#endif

#include <stdlib.h>
#include <stdio.h>
#include <fstream>
#include <vector>
#include <set>
#include <string>

#include "graph.h"
#include "view.h"


using namespace std;

Graph *graph;

#define min(x,y) (((x)<(y))? (x):(y))
#define max(x,y) (((x)>(y))? (x):(y))

Controller* controller;

StatManager *stats;


void deleteVisual() {
    delete stats;
    delete controller;
}

void initializeVisual() {
    stats = new StatManager("NodeStatManager");
    graph->setStatManager(stats);

    controller->registerProperty("signs", "Both");
    controller->registerProperty("signs", "Positive");
    controller->registerProperty("signs", "Negative");

    controller->registerProperty("layout", "None");
}


//extern View *createSensitivityPlot(string name);
extern void registerHistograms(Controller *controller);
extern void registerGraphSensitivity(Controller *controller);
extern void registerGraphBundling(Controller *controller);
extern void addFilterTools(Controller * controller);

extern Shortcut * createShortcutScatterplot(string name, string typeElement);
extern Shortcut * createShortcutGraph(string name);
extern Shortcut * createShortcutMatrix(string name);
extern Shortcut * createShortcutParallel();
extern Shortcut * createShortcutHistogram(string name, string typeElement);
extern Shortcut * createShortcutScatterplotMatrix(string name, string viewScatter, string viewHist, string typeElement);
extern void createCanvasShortcuts(Controller *controller);

class DummyShortcut :public Shortcut {
public:
	DummyShortcut(string name): Shortcut(name) {}
	virtual void run() {}	
};

Shortcut *createDummyShortcut(string name) {
	return new DummyShortcut(name);
}

void createShortcuts() {
	controller->addShortcut(createShortcutScatterplot("ScatterPlot", "node"));
	controller->addShortcut(createShortcutScatterplot("Edge ScatterPlot", "edge"));
	controller->addShortcut(createShortcutScatterplot("SensitivityPlot", "node"));
	controller->addShortcut(createShortcutParallel());
	controller->addShortcut(createShortcutHistogram("Node Histogram", "node"));
	controller->addShortcut(createShortcutHistogram("Edge Histogram", "edge"));
	controller->addShortcut(createShortcutGraph("SimpleGraphView"));
	controller->addShortcut(createShortcutGraph("GraphView"));
	controller->addShortcut(createShortcutMatrix("Matrix View"));
//	controller->addShortcut(createDummyShortcut("Layouts/Radial"));
//	controller->addShortcut(createDummyShortcut("Layouts/Hierarchical"));
//	controller->addShortcut(createDummyShortcut("Layouts/Force directed"));
//	controller->addShortcut(createDummyShortcut("Layouts/Linlog"));
//	controller->addShortcut(createDummyShortcut("Multiview/Scatterplot+Legend"));
	controller->addShortcut(createShortcutScatterplotMatrix("Multiview/Node Scatterplot Matrix", "ScatterPlot", "Node Histogram", "node"));
	controller->addShortcut(createShortcutScatterplotMatrix("Multiview/Edge Scatterplot Matrix", "Edge ScatterPlot", "Edge Histogram", "edge"));
	createCanvasShortcuts(controller);
}

int main(int argc, char *argv[]) {

	controller = new Controller();
	/**
  Register Properties
  **/
	controller->registerProperty("node", "Default");
	//controller->registerProperty("node", "Degree");
	controller->registerProperty("edge", "Default");
	controller->registerProperty("edge", "Multiplicity");
	controller->registerProperty("colormap", "Default");
	controller->registerProperty("operation", "Greater than");
	controller->registerProperty("operation", "Less than");
	controller->registerProperty("operation", "Equals");
	controller->registerProperty("operation", "Not equals");
	controller->registerProperty("operation", "Greater or equal than");
	controller->registerProperty("operation", "Less or equal than");

	controller->registerProperty("stringOperation", "Equals");
	controller->registerProperty("stringOperation", "Contains");
	controller->registerProperty("stringOperation", "Starts with");
	controller->registerProperty("stringOperation", "Ends with");
	
	/**
	 Register View Creators
	 **/
	controller->addViewCreator("SimpleGraphView", createSimpleGraphView);
	controller->addViewCreator("GraphView", createGraphView);
	controller->addViewCreator("ParallelCoords", createParallel);
	controller->addViewCreator("ScatterPlot", createScatter);

	controller->addViewCreator("Edge ScatterPlot", createEdgeScatter);
	controller->addViewCreator("Edge Correlation Plot", createEdgeCorrelation);
	controller->addViewCreator("MonopolyView", createMonopolyView);    
	controller->addViewCreator("SensitivityPlot", createSensitivityPlot);

#ifdef USING_FLOW_YES
    controller->addViewCreator("ScatterPlotMap", createMap); //yuhsuan
    controller->addViewCreator("SensitivityBaseView", createSensitivityBaseView);
    controller->addViewCreator("SensitivityBaseViewFan", createSensitivityFan);
    controller->addViewCreator("SensitivityBaseViewFanCircular", createSensitivityCircular);
    controller->addViewCreator("SensitivityRanking", createSensitivityRankingView);
    controller->addViewCreator("Node Properties Discrete Legend",createNodeDiscreteLegend);
    controller->addViewCreator("Edge Properties Discrete Legend",createEdgeDiscreteLegend);
#endif

	controller->addViewCreator("Node Legend", createNodeLegend);
	controller->addViewCreator("Edge Legend", createEdgeLegend);
	controller->addViewCreator("Discrete Node Legend", createDiscreteLegendNode);
	controller->addViewCreator("Discrete Edge Legend", createDiscreteLegendEdge);
	controller->addViewCreator("Matrix View", createMatrixView);
	registerHistograms(controller);


	ViewManager *viewManager = new ViewManager(controller);
	controller->setViewManager(viewManager);
	controller->addViewListener(viewManager);

	/**
  Register Layouts
  **/
	controller->addLayoutCreator("Default", createDefaultLayout);
	controller->addLayoutCreator("Force Directed", createForceDirectedLayout);
	controller->addLayoutCreator("LinLog", createLinlogLayout);
	controller->addLayoutCreator("Property", createPropertyLayout);
#ifdef USING_GMPUFM3
    controller->addLayoutCreator("FM3",createFM3Layout);
#endif

	printf("[Main]  Added controller\n");
	graph = new Graph(controller);
	controller->addGraph("graph", graph);

	/**
  Register Tools
  **/
	controller->addTool(new Betweenness(controller));
	controller->addTool(new Closeness(controller));
#ifdef USINGLAPACK
    controller->addTool(new EigenvectorCentrality(controller));
    controller->addTool(new MarkovCentrality(controller));
#endif
	controller->addTool(new EigenvectorCentralityEigen(controller));
	controller->addTool(new MarkovCentralityEigen(controller));
	controller->addTool(new ClusteringCoefficient(controller));
	controller->addTool(createMSTTool(controller));
	controller->addTool(createSkeletonTool(controller));
	addDataTools(controller);
	addFilterTools(controller);
	controller->addTool(new Sensitivity(controller));
	
#ifdef USING_LIBPNG
    addScreenShotTools(controller);
#endif
	controller->addTool(new Sensitivity(controller));

	/**
  Initialize Color maps
  **/
	initializeVisual();

	if(argc>1) {
			controller->readScript(argv[1]);
	}
	createShortcuts();
	
	graph->computeEdgeMinMaxFloat("Multiplicity");
	controller->addDataProperty("edge", "Multiplicity", PROPERTY_FLOAT);
	//graph->computeNodeMinMaxFloat("Degree"); //do this in importEdgeTable instead.

	QApplication app(argc, argv);
	QNWindow window(controller);
	window.setMinimumWidth(900);
	window.setMinimumHeight(500);
	window.show();
	QFontDatabase database;
	QStringList fonts =  database.families();
	for(int i=0; i<(int) fonts.size(); i++) {
			string c = fonts[i].toStdString();
			controller->registerProperty("font", c.c_str());
	}
	return app.exec();
}
